package model.locations;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import exceptions.InvalidDirectionException;
import exceptions.LockedDoorException;
import exceptions.NoDoorException;
import exceptions.OutOfBoundsException;
import exceptions.TalkToPerson;
import exceptions.ThingNotInArea;




import model.boundary.PassThroughBoundary;
import model.commands.Command;
import model.locations.Area;
import model.locations.Level;
import model.locations.Path;
import model.locations.Wall;
import model.things.Extra;
import model.things.Thing;
import model.things.You;
import net.slashie.libjcsi.CSIColor;
import net.slashie.libjcsi.ConsoleSystemInterface;
import net.slashie.libjcsi.wswing.WSwingConsoleInterface;

public class World implements _World{
	
	int level = 1;
	boolean nextLevel=false;
	Level currentLevel;	
	HashMap<Integer,Level> levelStore = new HashMap<Integer,Level>();

	You player;
	                  
	public World(){
		
		System.out.println("Creating World");
		
		try {
			player=new You("player",2,2);
			Level level1=new Level(4,'s',3,2,0,0,player);
			//add "vertical" boundary
			level1.addBoundary(2, 3,true,false);
		
			level1.addThing(new Extra("LUKE",2,2));
			level1.addThing(new Extra("SAUL",2,2));
		
			levelStore.put(1, level1);
			
			Level level2= new Level(3,'s',2,2,0,0,player);
			levelStore.put(2, level2);
			
			currentLevel=levelStore.get(1);
		
		} catch (OutOfBoundsException e) {
			e.printStackTrace();
		} catch (InvalidDirectionException e) {
			e.printStackTrace();
		}
	}	
	
//this method ensures that the value nextLevel goes back to false after it has been used
	public boolean getNextLevel() {
		if(nextLevel){
			nextLevel=false;
			return true;
		}
		return nextLevel;
	}
	
	
	public String toString() {
		
		try{
			String r = "";
			int x = player.getX(), y = player.getY();
			for( int i = 0; i <= currentLevel.getSize(); i++ ) {
				
				for( int j = 0; j < currentLevel.getSize(); j++){
						r+=" ";
						//System.out.println("i"+i+"j"+j);
						if(currentLevel.getStatusBoundary(i, j,true)=="BLOCKED") r+="#";
						if(currentLevel.getStatusBoundary(i, j,true)=="OPEN") r+="O";
						if(currentLevel.getStatusBoundary(i, j,true)=="CLOSED") r+="C";
						if(currentLevel.getStatusBoundary(i, j,true)=="LOCKED") r+="L";
						if(currentLevel.getStatusBoundary(i, j,true)=="PASS") r+="/";
						if(currentLevel.getStatusBoundary(i, j,true)=="EXIT") r+="^";
					}
				r+="\n";
				if(i<currentLevel.getSize()){
					for( int k = 0; k < currentLevel.getSize()+1; k++){
						if(currentLevel.getStatusBoundary(i, k,false).equals("LOCKED")) r+="L";
						if(currentLevel.getStatusBoundary(i, k,false)=="OPEN") r+="O";
						if(currentLevel.getStatusBoundary(i, k,false)=="CLOSED") r+="C";
						if(currentLevel.getStatusBoundary(i, k,false)=="BLOCKED") r+="#";
						if(currentLevel.getStatusBoundary(i, k,false)=="PASS") r+="/";
						if(currentLevel.getStatusBoundary(i, k,false)=="EXIT") r+="^";
						if(k<currentLevel.getSize()){
							if(currentLevel.getTypeArea(i, k) == "BLOCK") r+="N";
							if( i == x && k == y) {r+="P"; continue;}
							if(currentLevel.getTypeArea(i, k) == "PATH") r+="A";
						}
					}
				}
				r+="\n";
			}
			return r;
		}
		catch(OutOfBoundsException ex){
			return "out of bounds";
		}
		}
	
	/* This serves as an example, it is probably in the wrong place
	 * I like it when things know how to draw themselves
	 */
	public void drawInSwing(ConsoleSystemInterface csi){
		// These are offsets
		int x = 5;
		int y = 10;
		// This clears the screen
		csi.cls();
		// These demo text
		csi.print(1,2,"Wow!",CSIColor.WHITE);
		csi.print(5,2, "Look!",CSIColor.ALIZARIN);
		csi.print(10,2, "Talk!",CSIColor.JADE);
		csi.print(15,2, "Luke?",CSIColor.POWDER_BLUE);
		int px = player.getX(), py = player.getY();
		try{
			for( int i = 0; i < currentLevel.getSize(); i++ ) {
				for( int j = 0; j < currentLevel.getSize(); j++){
					if(currentLevel.getTypeArea(i, j) == "BLOCK"){
						csi.print(j+y, i+x,"#",ConsoleSystemInterface.RED);
					}
					if( i == px && j == py) {
						csi.print(j+y, i+x, "@", ConsoleSystemInterface.BLUE);
						continue;
					}
					if(currentLevel.getTypeArea(i, j) == "PATH"){
						csi.print(j+y, i+x, ".", ConsoleSystemInterface.GRAY);
					}
				}
			}
			}catch(OutOfBoundsException ex){
				System.out.println("out of bounds");
			}
		csi.print(15,15,"This map updates when you move. Nothing else really happens.",CSIColor.SCHOOL_BUS_YELLOW,CSIColor.CHOCOLATE);
		// This displays the screen, things are not drawn to screen until this is called.
        csi.refresh();
	}

	public String goDirection(String d) {
		//I added this niftly position finder
		
		int i = player.getX(), j = player.getY();
		System.out.println("going "+d+" from " + i + " " + j);
		try{
				
			if(d.equals("EAST")){
				return getDirection(0,1,0,1,false);
			}
			else if(d.equals("WEST")){
				return getDirection(0,0,0,-1,false);
			}
			else if(d.equals("SOUTH")){
				return getDirection(1,0,1,0,true);
			}
			else if(d.equals("NORTH")){
				return getDirection(0,0,-1,0,true);
			}
			return "not valid direction";
		}
		catch(OutOfBoundsException ex){
			System.out.println("out of bounds");
			return "out of bounds";
		}	
	}
	
	public String  getDirection(int changeBX, int changeBY,int X,int Y,boolean vertical) throws OutOfBoundsException{
	
		int i = player.getX(), j = player.getY();
		int boundaryX=changeBX+i;
		int boundaryY=changeBY+j;
		int areaX=i+X,areaY=j+Y;
				if(currentLevel.getStatusBoundary(boundaryX, boundaryY,vertical)=="BLOCKED"||currentLevel.getStatusBoundary(boundaryX, boundaryY,vertical)=="LOCKED"||currentLevel.getStatusBoundary(boundaryX, boundaryY,vertical)=="CLOSED") 
					return currentLevel.getDescriptionBoundary(boundaryX, boundaryY,vertical);
				if(currentLevel.getStatusBoundary(boundaryX, boundaryY,vertical)=="EXIT"){
					nextLevel=true;
					return "do you want to proceed to the next level?(y/n)";
				}
				//Check player can go in that direction.
				//if so, remove from old location
				if( currentLevel.getTypeArea(areaX, areaY) == "BLOCK" ) return currentLevel.getDescriptionArea(areaX,areaY);
					//update player position too. easier to quickly access position
				player.move(X, Y);
				currentLevel.update();
				return currentLevel.getDescriptionArea(areaX, areaY);
}
	
	
	public String getDescription(){
		
		int i = player.getX(), j = player.getY();
		try {
			return currentLevel.getDescriptionArea(i, j);
		} catch (OutOfBoundsException e) {
			e.printStackTrace();
			return "out of bounds";
		}
	}
	
	
	public boolean getThing(String objectLabel,String type) {
		
		return currentLevel.getThingType(objectLabel,type);
}
	
	
	public boolean haveYouObatinedItem(String item){
		
		//need to implement
		return false;
	}
	
	public String openCloseDoor(boolean open){
		
		try {
			if(currentLevel.openAndClose(open)&&open)
				return "you have opened the door";
			else if(currentLevel.openAndClose(open)&&!open)
				return "you have closed the door";
			else if(!currentLevel.openAndClose(open)&&open)	
				return "the door is already open";
				return "the door is already closed";
		} catch (LockedDoorException e) {
				return "The door is locked";
		}catch (NoDoorException e){
			return "there is no door here";
		}
	}
	

	public void changeSetting(){
		level+=1;
		int i = player.getX(), j = player.getY();
		currentLevel.removeThing(player.getLabel(), i, j);
		
		currentLevel=levelStore.get(2);
		player.put(currentLevel.getStartX(), currentLevel.getStartY());
		currentLevel.placePlayer(player);
	}
}
